﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Concurrent;
using System.Collections;
using CSharpExperiments.Metaprogramming;
using CSharpExperiments.Collections;

namespace CSharpExperiments
{
	public class Multitons : Singleton<Multitons>
	{
		private readonly ConcurrentDictionary<Type, ArgList, Object> values
			= new ConcurrentDictionary<Type, ArgList, Object>();

		public T Get<T>(params Object[] args)
		{
			return (T) values.GetOrAdd(typeof(T), new ArgList(args), (t, a) => Meta.New<T>(a.Args));
		}

		private class ArgList
		{
			private Object[] args;

			public ArgList(params Object[] args)
			{
				this.args = args.ToArray();
			}

			public Object[] Args
			{
				get { return args.ToArray(); }
			}

			public override bool Equals(object obj)
			{
				if (obj == this) return true;
				if (obj == null) return false;
				if (! (obj is ArgList)) return false;
				var that = obj as ArgList;
				return ((IStructuralEquatable) args).Equals(that.args, EqualityComparer<object>.Default);
			}

			public override int GetHashCode()
			{
				return ((IStructuralEquatable) args).GetHashCode(EqualityComparer<object>.Default);
			}
		}
	}

	public abstract class Multiton<T> where T : Multiton<T>
	{
		public static T Current(params Object[] args)
		{
			return Multitons.Current.Get<T>(args);
		}
	}
}
